AWS Step Functionsステートマシンが時間内に正常終了したか監視してみた
定期実行しているAWS Step Functionsステートマシンが想定時間内に正常終了したか、EventBridge SchedulerとLambdaを使って監視する方法を紹介します。
前提とするステートマシン
依存関係のあるステートマシンや日次バッチが時間内に完了したか確認したいケースを想定し、ステートマシンの実行間隔が十分に長い定期実行は想定していますが、同じステートマシンがオーバーラップして実行されることは想定していません。
具体的には、1日に1回呼び出され、処理に3時間かかるステートマシンは想定内ですが、2時間間隔で呼び出され、処理に3時間かかり、実行がオーバーラップするようなステートマシンは想定外です。
また、EXPRESS ステートマシンも対象外です。
監視方法
Step Functions には ListExecutions という API が存在し、実行履歴を最新のものから時系列順に取得できます。
ステートマシンが午前0時に呼び出され、2時間には終わっているはずであれば、午前2時にLambda関数で最新のステータスを確認し、正常終了していなければ、SNSに通知します。
以上をまとめたCloudFormationテンプレートを本文最後に記載しています。
StepFunctions:ListExecutions API について
StepFunctions:ListExecutions APIを利用すると、実行履歴を時系列順に取得できます。
最新の実行だけを取得したい場合、 maxResults=1
と個数を指定します。
各実行のステータスは以下のいずれかの値をとります。
- SUCCEEDED
- RUNNING
- FAILED
- TIMED_OUT
- ABORTED
SUCCEEDED
ステータスだけが正常終了です。
RUNNING
ステータスは遅延を意味し、残りのステータスは異常終了です。
AWS CLI からは、以下のように呼び出します。
$ aws stepfunctions list-executions \ --state-machine-arn arn:aws:states:eu-west-1:12345789012:stateMachine:XXX \ --max-items 1
Python SDKからは、以下のように呼び出します。
sfn = boto3.client('stepfunctions') response = sfn.list_executions( stateMachineArn = "arn:aws:states:eu-west-1:12345789012:stateMachine:XXX", maxResults = 1 ) latest_activity = response['executions'][0] status = latest_activity['status'] if status != 'SUCCEEDED': # TODO:異常系処理 pass
定期呼び出し方法
EventBridge SchedulerあるいはEventBridge Ruleを利用すると、簡単に定期呼び出しできます。 後発のEventBridge Schedulerは、実行ウィンドウやタイムゾーンの指定など、より柔軟な設定が可能です。
各ステートマシンには
- ステートマシンの実行
- ステートマシン実行の監視
の2種類のスケジュール設定が必要です。
EventBridge SchedulerはたくさんのAWS APIをターゲットとして直接呼び出せます。
残念ながら、StepFunctions:ListExecutions APIは対象外だったため、Lambdaを挟んでいます。
通知
最新の実行が正常終了していなかった場合、SNS等を利用してエラー通知しましょう。
異常終了に限定すると、デッドレターキュー(DLQ)を利用すると、異常終了を速やかに検知できます。
CloudWatchメトリクスは利用しづらい
CloudWatchメトリクスには、ExecutionsSucceeded
やExecutionsFailed
やExecutionTime
のように、各ステータスに対応するメトリクスが存在します。
ステータス毎にメトリクスが異なり、処理が遅延(RUNNING
)している場合、実際に処理が終了するまで処理時間(ExecutionTime
)のデータポイントが発生しななくて監視できないことから、今回のようにSUCCEEDED
以外のすべてのステートを補足したいユースケースには向かないでしょう。
最後に
AWS Step Functionsステートマシンの実行をStepFunctions:ListExecutions APIを使って監視する方法を紹介しました。
実行スケジュールがシンプルであれば、それなりにカバーできるのではないかと思います。
それでは。
付録:CloudFormationテンプレート
今回紹介した構成のCloudFormationテンプレートです。
監視対象のステートマシンとエラー通知先のSNSトピックはすでに存在するものとします。
Lambdaに渡すステートマシンはEventBridgeのInputに利用し、Lambdaから送信するSNSトピックはLambdaの環境変数で管理しています。
このテンプレートでは
- EventBridge Scheduler
- EventBridge Rule
の2種類のスケジュールを作成しています。
好きな方を残してください。
AWSTemplateFormatVersion: 2010-09-09 Parameters: SnsArn: Type: String SfnArn: Type: String Resources: LambdaFunction: Type: AWS::Lambda::Function Properties: Handler: index.lambda_handler Code: ZipFile: !Sub | import boto3 import os sfn = boto3.client('stepfunctions') sns = boto3.client('sns') SNS_ARN = os.environ['SNS_ARN'] def lambda_handler(event, context): sfn_arn = event['SFN_ARN'] response = sfn.list_executions( stateMachineArn = sfn_arn, maxResults = 1 ) print(response) if response['executions']: latest_activity = response['executions'][0] status = latest_activity['status'] if status != 'SUCCEEDED': print("last activity didn't succeed") sns.publish( TargetArn=SNS_ARN, Message='state machine {} is in the state {}'.format(sfn_arn, status) ) else: print("last activity succeeded") else: print("No acitivity found") Runtime: python3.9 Timeout: 30 Environment: Variables: SNS_ARN : !Ref SnsArn Role: !GetAtt LambdaFunctionRole.Arn LambdaFunctionRole: Type: AWS::IAM::Role Properties: ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: function-nsn-policy PolicyDocument: Version: 2012-10-17 Statement: - Resource: !Ref SnsArn Effect: Allow Action: - sns:Publish - Resource: "*" Effect: Allow Action: - states:ListExecutions EventSchedule: Type: AWS::Scheduler::Schedule Properties: GroupName: default ScheduleExpression: cron(0 0 * * ? *) ScheduleExpressionTimezone: Japan FlexibleTimeWindow: Mode: "OFF" State: ENABLED Target: Arn: !GetAtt LambdaFunction.Arn Input: !Sub '{"SFN_ARN": "${SfnArn}"}' RoleArn: !GetAtt EventScheduleRole.Arn EventScheduleRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - scheduler.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: CallStepFunctions PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - lambda:InvokeLambda Resource: - !GetAtt LambdaFunction.Arn EventBridgeRule: Type: AWS::Events::Rule Properties: EventBusName: default ScheduleExpression: cron(0 15 * * ? *) State: ENABLED Targets: - Arn: !GetAtt LambdaFunction.Arn Id: LambdaFunction Input: !Sub '{"SFN_ARN": "${SfnArn}"}' LambdaEvent: Type: AWS::Lambda::Permission Properties: FunctionName: !Ref LambdaFunction Action: lambda:InvokeFunction Principal: events.amazonaws.com SourceArn: !GetAtt EventBridgeRule.Arn
AWS::Scheduler::Schedule
の Mode: "OFF"
に関して、ダブルクオートを外して Mode: OFF
とすると、次のエラーが発生します。
Properties validation failed for resource EventSchedule with message: #/FlexibleTimeWindow/Mode: #: only 1 subschema matches out of 2 #/FlexibleTimeWindow/Mode: failed validation constraint for keyword [enum]
ご注意ください。